﻿using System.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WatiN.Core;
using SpecFlowAlias = TechTalk.SpecFlow;
using Liekhus.Testing.BDD.Core;
using TechTalk.SpecFlow;
using System.Threading;
using System;
using System.IO;
using System.Text.RegularExpressions;
using WatiN.Core.Exceptions;
using DevExpress.Web.ASPxEditors;

namespace Liekhus.Testing.BDD.Web
{
    //[Binding]
    public class StepDefinition : BaseStepDefinitions
    {
        private string GetUrl(string page)
        {
            string url = string.Empty;
            if (ConfigurationManager.AppSettings[string.Format("Page.{0}", page)] != null)
            {
                url = ConfigurationManager.AppSettings[string.Format("Page.{0}", page)].ToString();
            }

            return url;
        }
        
        private void OpenPage(string page)
        {
            string url = GetUrl(page);

            if (string.IsNullOrEmpty(url) == false)
            {
                WebBrowser.Current.GoTo(string.Format("{0}{1}", ConfigurationManager.AppSettings["Site.Url"].ToString(), url));
                WebBrowser.Current.WaitForComplete();
            }
        }

        public override void GivenIAmLoggedInAs(string username)
        {
            WhenIAmAtTheScreen("Portal");
            SpecFlowAlias.Table table = new SpecFlowAlias.Table("Field", "Value");
            table.AddRow("UserName", string.Format("{0}\\{1}", ConfigurationManager.AppSettings[string.Format("Login.{0}.Domain", username)], ConfigurationManager.AppSettings[string.Format("Login.{0}.Username", username)]));
            table.AddRow("Password", ConfigurationManager.AppSettings[string.Format("Login.{0}.Password", username)]);
            WhenIEnterTheFollowingInformation(table);
            WhenIClickTheLink("Log On");
        }

        [Given("I close the browser")]
        public void GivenICloseTheBrowser()
        {
            WebBrowser.Current.Close();
        }

        //[Given("I am at the \"(.*)\" screen")]
        public override void WhenIAmAtTheScreen(string page)
        {
            OpenPage(page);
        }

        private Link LocateLink(string text)
        {
            Link link = WebBrowser.Current.Link(Find.ByText(text));

            if (link == null || link.Exists == false)
            {
                link = WebBrowser.Current.Link(Find.ByAlt(text));
            }

            return link;
        }

        //[When("I click the \"(.*)\" link")]
        public override void WhenIClickTheLink(string text)
        {
            var link = LocateLink(text);

            if (link.Exists == false)
            {
                Assert.Fail(string.Format("Could not find {0} link on the page.", text));
            }
            link.Click();

            WebBrowser.Current.WaitForComplete();
            WaitForAsyncPostBackToComplete();
        }

        private Button LocateButton(Form form, string text)
        {
            Button button = form.Button(Find.ByValue(text));

            if (button.Exists == false)
            {
                button = form.Button(Find.ByAlt(text));
            }
            
            if (button.Exists == false)
            {
                button = form.Button(e => e.Text.Contains(string.Format("_{0}", text)));
            }

            return button;
        }

        private Image LocateImage(string text)
        {
            Image image = WebBrowser.Current.Image(Find.ByValue(text));

            if (image.Exists == false)
            {
                image = WebBrowser.Current.Image(Find.ByAlt(text));
            }

            if (image.Exists == false)
            {
                image = WebBrowser.Current.Image(e => e.Text.Contains(string.Format("_{0}", text)));
            }

            return image;
        }

        [When("I click the \"(.*)\" button on the popup")]
        public void WhenIClickTheButtonOnPopup(string text)
        {
            //Button button = LocateButon(text);
            WhenIClickTheButton(text);
            WaitForAsyncPostBackToComplete();
        }

        protected static void WaitForAsyncPostBackToComplete()
        {
            bool isInPostback = true;
            while (isInPostback)
            {
                try
                {
                    isInPostback = Convert.ToBoolean(WebBrowser.Current.Eval("Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack();"));
                    if (isInPostback)
                    {
                        Thread.Sleep(200); //sleep for 200ms and query again 
                    }
                }
                catch //sys is probably undefined.  Let's drop out and continue on
                {
                    isInPostback = false;
                }
            }
        }

        [When("I click the \"(.*)\" button in the \"(.*)\" popup")]
        public void WhenIClickTheButtonInThePopup(string text, string popupName)
        {
            Frame frame = FindPopup(popupName);
            ClickButton(frame.Forms[0], text);
        }

        //[When("I click the \"(.*)\" button")]
        public override void WhenIClickTheButton(string text)
        {
            ClickButton(WebBrowser.Current.Forms[0], text);
        }

        private Button ClickButton(Form form, string text)
        {
            WebBrowser.Current.WaitForComplete();
            Button button = LocateButton(form, text);

            if (button.Exists == false)
            {
                Assert.Fail(string.Format("Could not find {0} button on the page.", text));
            }
            button.Click();

            WebBrowser.Current.WaitForComplete();
            WaitForAsyncPostBackToComplete();

            return button;
        }

        [When("I click the \"(.*)\" image")]
        public void WhenIClickTheImage(string text)
        {
            WebBrowser.Current.WaitForComplete();
            Image image = LocateImage(text);

            if (image.Exists == false)
            {
                Assert.Fail(string.Format("Could not find {0} image on the page.", text));
            }
            image.Click();

            WebBrowser.Current.WaitForComplete();
            WaitForAsyncPostBackToComplete();
        }

        private Frame LocateFrame(string frameKey)
        {
            WebBrowser.Current.WaitForComplete();
            Frame frame = null;
            try
            {
                frame = WebBrowser.Current.Frame(Find.ByValue(frameKey));
            }
            catch (FrameNotFoundException e)
            {
                frame = WebBrowser.Current.Frame(new Regex(string.Format("[a-zA-Z0-9\\$]*{0}[a-zA-Z0-9\\$]*", frameKey)));
            }

            return frame;
        }

        private Frame FindPopup(string popupName)
        {
            string frameKey = string.Empty;
            if (ConfigurationManager.AppSettings[string.Format("Popup.{0}", popupName)] != null)
            {
                frameKey = ConfigurationManager.AppSettings[string.Format("Popup.{0}", popupName)].ToString();
            }
            return LocateFrame(frameKey);
        }

        [When("I enter the following information in the \"(.*)\" popup")]
        public void WhenIEnterTheFollowingInformationInThePopup(string popupName, SpecFlowAlias.Table table)
        {
            Frame frame = FindPopup(popupName);

            frame.DomContainer.WaitForComplete();
            FillForm(table, frame.Forms[0]);
        }

        private void FillForm(SpecFlowAlias.Table table, Form form)
        {
            form.DomContainer.WaitForComplete();

            foreach (var row in table.Rows)
            {
                bool isValid = false;
                isValid = FillTextBox(form, row["Field"], row["Value"]);
                if (isValid == false) { isValid = FillSelectList(form, row["Field"], row["Value"]); }
                if (isValid == false) { isValid = FillDateField(form, row["Field"], row["Value"]); }

                if (isValid == false)
                {
                    Assert.Fail(string.Format("Field {0} could not be found on the page.", row["Field"]));
                }
            }
        }

        //[When("I enter the following information")]
        public override void WhenIEnterTheFollowingInformation(SpecFlowAlias.Table table)
        {
            FillForm(table, WebBrowser.Current.Forms[0]);
        }

        private TextField LocateTextBox(Form form, string name)
        {
            TextField field = form.TextField(Find.ByName(name));

            if (field.Exists == false)
            {
                field = form.TextField(new Regex(string.Format("[a-zA-Z0-9\\$]*{0}[a-zA-Z0-9\\$]*", name)));
            }

            return field;
        }

        private Element LocateDateField(Form form, string name)
        {
            Element field = form.Element(Find.ByName(name));

            if (field.Exists == false)
            {
                field = form.Element(new Regex(string.Format("[a-zA-Z0-9\\$]*{0}[a-zA-Z0-9\\$]*", name)));
            }

            return field;
        }

        private bool FillTextBox(Form form, string name, string value)
        {
            bool isValid = false;

            TextField field = LocateTextBox(form, name);
            isValid = field.Exists && field.ReadOnly == false;
            if (isValid)
            {
                field.TypeText(value);
            }
            return isValid;
        }

        private bool FillDateField(Form form, string name, string value)
        {
            bool isValid = false;

            name = name + "_STATE";
            value = string.Format("{0}:{1}", DateTime.Now.ToString("MM/dd/yyyy"), DateTime.Parse(value).ToString("MM/dd/yyyy"));

            Element field = LocateDateField(form, name);
            isValid = field.Exists;
            if (isValid)
            {
                field.SetAttributeValue("Value", value);
            }
            return isValid;
        }

        private SelectList LocateSelectList(Form form, string name)
        {
            SelectList field = form.SelectList(Find.ByName(name));

            if (field.Exists == false)
            {
                field = form.SelectList(new Regex(string.Format("[a-zA-Z0-9\\$]*{0}[a-zA-Z0-9\\$]*", name)));
            }

            return field;
        }

        private bool FillSelectList(Form form, string name, string value)
        {
            bool isValid = false;

            SelectList field = LocateSelectList(form, name);
            isValid = field.Exists;
            if (isValid)
            {
                field.Select(value);
            }
            return isValid;
        }

        private bool VerifyTextBoxValue(Form form, string name, string value)
        {
            bool isValid = false;

            TextField field = LocateTextBox(form, name);
            isValid = field.Exists;
            if (isValid)
            {
                isValid = (field.Value == value);
            }
            return isValid;
        }

        private bool VerifySelectListValue(Form form, string name, string value)
        {
            bool isValid = false;

            SelectList field = LocateSelectList(form, name);
            isValid = field.Exists;
            if (isValid)
            {
                isValid = (field.SelectedItem == value);
            }
            return isValid;
        }

        //[Then("I should see the following results")]
        public override void ThenIShouldSeeTheFollowingResults(SpecFlowAlias.Table table)
        {
            WebBrowser.Current.WaitForComplete();
            Form form = WebBrowser.Current.Forms[0];
            foreach (var row in table.Rows)
            {
                bool isValid = false;

                isValid = VerifyTextBoxValue(form, row["Field"], row["Value"]);
                if (isValid == false) { isValid = VerifySelectListValue(form, row["Field"], row["Value"]); }
                if (isValid == false)
                {
                    Assert.Fail(string.Format("Could not find {0} field on the page.", row["Field"]));
                }
                Assert.IsTrue(isValid == true, string.Format("Field {0} does not contain a value of {1}", row["Field"], row["Value"]));
            }
        }

        [Then("I should be on the \"(.*)\" screen")]
        public void ThenIShouldBeOnTheScreen(string page)
        {
            //it appears that on a redirect the web page cache to check is not catching up.. let's see if we can encourage it to be correct.
            //walking through the test is fine, but at full speed it trips!!!
            WaitForAsyncPostBackToComplete();
            WebBrowser.Current.WaitForComplete();
            
            string url = GetUrl(page);
            string fileName = fileName = Path.GetFileName(WebBrowser.Current.Uri.LocalPath);
            
            if (string.IsNullOrEmpty(fileName) == false && string.IsNullOrEmpty(url)==false)
            {
                Assert.IsTrue(url.ToLower().Equals(fileName.ToLower()));
            }
            else
            {
                Assert.Fail("Could not determine url and/or filename.");
            }
        }

        //[Then("I should see the following on the screen")]
        public override void ThenIShouldSeeTheFollowingOnScreen(SpecFlowAlias.Table table)
        {
            WaitForAsyncPostBackToComplete();
            WebBrowser.Current.WaitForComplete(5);

            foreach (var row in table.Rows)
            {
                var value = row["Value"];
                Assert.IsTrue(WebBrowser.Current.ContainsText(value), string.Format("Could not find text '{0}' on the page.", value));
            }
        }

    }
}